热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

上界|下界_重学C++:笔记C++基础容器&C++指针引用

篇首语:本文由编程笔记#小编为大家整理,主要介绍了重学C++:笔记C++基础容器&C++指针引用相关的知识,希望对你有一定的参考价值。文章目录

篇首语:本文由编程笔记#小编为大家整理,主要介绍了重学C++:笔记C++基础容器&C++指针引用相关的知识,希望对你有一定的参考价值。



文章目录


    • 第4章 C++基础容器
      • 4.1 序列容器--数组
      • 4.2 数组的增删改查及二维数组
      • 4.3 动态数组vector
      • 4.4 字符串

    • 第5章 彻底学会 C++ 指针,引用
      • 5.1 指针的概念
      • 5.2 指针的基本操作
      • 5.3 CPP程序的存储区域划分
      • 5.4 CPP动态分配和回收原则
      • 5.5 RAII初步
      • 5.6 C++中几种变量的对比
      • 5.7 内存泄漏
      • 5.8 智能指针
      • 5.9 引用




第4章 C++基础容器


4.1 序列容器–数组



off-by-one error数组下标


off-by-one error (差一错误)

数组中通过左闭右开的方式可以避免上述错误,如:

for (int i &#61; 0; i <10 ; i&#43;&#43;)
cout <


数组设计的原则


从0开始&#xff0c;使用非对称空间&#xff1a;

让下界&#xff08;左侧&#xff09;可以取到值&#xff0c;让上界&#xff08;右侧&#xff09;取不到值&#xff1b;

好处&#xff1a;


  • 取值范围的大小&#xff1a;上界-下界
  • 如果取值范围为空&#xff0c;上界值&#61;&#61;下界值
  • 及时取值范围为空上界值也永远不可能小于下界值

4.2 数组的增删改查及二维数组



二维数组设计的tips&#xff08;循环时尽可能要满足空间局限性&#xff09;



  • 在一个小的时间窗口内&#xff0c;访问的变量地址越接近越好&#xff0c;这样执行速度快
  • 一般来说&#xff0c;需要将最长的循环放最内层&#xff0c;最短的循环放在最外层&#xff0c;以减少CPU跨层的次数

4.3 动态数组vector



使用前的准备&#xff08;引入头文件和namespace&#xff09;


#include
using namespace std;


相关方法


vector vec &#61; 1, 2, 3;
//在尾部插入元素
vec.push_back(4);
//在中间进行元素插入
vec.insert(vec.end()-1, 4); //在尾部前一个位置插入4
//删除尾部元素
vec.pop_back();
//删除中间元素
vec.erase(vec.end()-1); //删除尾部前一个位置的元素
//获取当前容量
vec.capacity();
//获取已经存储的元素个数
vec.size();

4.4 字符串



字符串变量



  • 字符串是以空字符&#xff08;’\\0’&#xff09;结束的字符数组
  • 空字符自动添加到字符串的内部表示中
  • 在声明字符串变量时&#xff0c;应该位这个空结束符预留一个额外元素的空间&#xff0c;如&#xff1a;char s [11] &#61; “helloworld” ;


Unicode编码


Unicode编码&#xff1a;最初的目的是把世界上的文字都映射到一套字符空间中


  • UTF-8
  • UTF-16
    • UTF-16BE
    • UTF-16LE
  • UTF-32
    • UTF-32BE
    • UTF-32LE

编码错误的根本原因在于编码方式和解码方式的不统一

Windows的文件可能有BOM&#xff08;byte order mark&#xff09;&#xff0c;如果要在其他平台使用&#xff0c;可以去掉BOM



字符串的指针表示


指针所指的区域能否改变取决于指针指向的那块区域是否为可变的&#xff0c;如若指向的为常量&#xff0c;则不可变&#xff0c;而若指向的区域为变量&#xff0c;那么可以改变

字符串数组数组名定义了之后就不可变&#xff0c;但是数组里的值可变&#xff0c;而定义一个指针&#xff0c;指针变量的值是可变的&#xff0c;但是若其指向的为常量&#xff0c;那么则指针指向的字符串的值不可变



字符串的基本操作


包含在头文件


  1. 字符串长度&#xff1a;strlen(s)

    返回字符串s的长度&#xff08;s的长度不包括 ’ \\0 &#39;&#xff09;

    区别sizeof()&#xff0c;sizeof()计算的为占用的空间&#xff0c;strlen()计算的为字符串长度

  2. 字符串比较&#xff1a;strcmp(s1, s2)

    若s1和s2 是相同的&#xff0c;则返回0&#xff1b;

    若s1

    若s1 > s2 则返回值大于0

  3. 字符串拷贝&#xff1a;strcpy(s1, s2)

    复制字符串s2到字符串s1

  4. 复制指定长度字符串&#xff1a;strncpy(s1, s2, n)

    将字符串s2中前n个字符串拷贝到s1中

  5. 字符串拼接&#xff1a;strcat(s1, s2)

    将字符串s2拼接到s1之后

  6. 查找字符串&#xff1a;strchr(s1, ch)

    指向字符串s1中字符ch的第一次出现的位置

  7. 查找字符串&#xff1a;strstr(s1, s2)

    指向字符串s1中字符串s2的第一次出现的位置

进行底层操作时&#xff0c;为避免编译器错误发出报错&#xff0c;可添加宏&#xff1a;_CRT_SECURE_NO_WARNINGS



缓冲区溢出问题


举例&#xff1a;在进行字符串拼接的操作时&#xff0c;若拼接长度过大而超出原本的长度&#xff0c;那么就有可能把存储区原本存储的信息改变&#xff0c;造成逻辑的改变

解决方法&#xff1a;养成进行边界判断的习惯&#xff0c;另外也存在更安全的API可供调用&#xff0c;如strcpy()更为安全的版本为strcpy_s()&#xff0c;其他几个字符串操作函数的安全版本均为*_s()&#xff0c;例如strcat_s(s1, size, s2,)&#xff0c;当s2的长度大于size时&#xff0c;那么会报错&#xff0c;无法运行



string简介


#include
using namespace std;
//定义字符串变量
string s;//定义空字符串
string s &#61; "helloworld";//定义并初始化
string s ( "helloworld" );
string s &#61; string( "helloworld" );

字符串相关函数&#xff1a;


  • 获取字符串的长度

    s.length()//字符串长度
    s.size()//同上
    s.capacity()//字符串s所占空间大小

  • 字符串比较&#xff1a;&#61; &#61; ! &#61; > > &#61; <<&#61;

    string s1 &#61; "hello", s2 &#61; "world";
    cout <<(s1 &#61;&#61; s2) <cout <<(s1 !&#61; s2) <

字符串的常用操作&#xff1a;

//转换为C风格的字符串
const char *c_str1 &#61; s1.c_str();
//随机访问&#xff08;获取字符串中某个字符&#xff09;&#xff1a;[]
string s &#61; "hello";
cout <//字符串拷贝&#xff1a;&#61;
string s1 &#61; "hello";
string s2 &#61; s1;
//字符串拷贝&#xff1a;&#43;、&#43;&#61;
string s1 &#61; "hello", s2 &#61; "world";
string s3 &#61; s1 &#43; s2;//s3:helloworld
s1 &#43;&#61; s2;//s1:helloworld

总结&#xff1a;string结合了C&#43;&#43;的新特性&#xff0c;使用起来比原始的C风格更安全和方便&#xff0c;对性能要求不是特别高的常见情况可以使用


第5章 彻底学会 C&#43;&#43; 指针&#xff0c;引用


5.1 指针的概念



C&#43;&#43;中内存单元内容与地址


指针本身就是一个变量&#xff0c;其符合变量定义的基本形式&#xff0c;它存储的是值的地址。对类型T&#xff0c;T*是“到T的指针”类型&#xff0c;一个类型为T*的变量能保存一个类型T的对象的地址

通过一个指针访问它所指向的地址的过程称为间接访问或者引用指针&#xff0c;这个用于执行简介访问的操作符是单目运算符*

一个变量有三个信息&#xff1a;


  • 变量的地址信息
  • 变量所存的信息
  • 变量的类型


左值与右值


左值&#xff1a;编译器为其单独分配了一块存储空间&#xff0c;可以取其地址的&#xff0c;左值可以放在赋值运算符的左边&#xff08;最常见的情况如函数和数据成员的名字&#xff09;

右值&#xff1a;指数据本身&#xff0c;不能取到其自身地址&#xff0c;右值只能在赋值运算符右边&#xff08;右值是没有标识符&#xff0c;不可以取地址的表达式&#xff0c;一般也称之为临时对象&#xff09;



一般指针数组和指针数组


指针的数组与数组的指针&#xff1a;


  • 指针的数组&#xff08;定义的是一个包含n个元素数组&#xff0c;数组里存放的都是T类型的指针&#xff09;&#xff1a;T* t[n]
  • 数组的指针&#xff08;定义了一个指针&#xff0c;指针指向一个包含n个T类型元素的数组&#xff09;&#xff1a;T(*t) [n]&#xff08;注意[]的优先级比较高&#xff09;

访问数组的指针所指向的数组的元素时&#xff0c;如&#xff1a;(*t)[3]表示指针所指向的数组的下标为3的元素



const与指针


char const *pStr1 &#61; "helloworld";
char* const pStr2 &#61; "helloworld";//pStr2不可改
char const* const pStr3 &#61; "helloworld";//pStr3不可改

关于const修饰符的部分&#xff1a;


  • 看左侧最近的部分
  • 如果左侧没有&#xff0c;则看右侧

如pStr1指针所指向的地址的内容不能变&#xff0c;pStr2指针指向的地址不能变&#xff0c;而pStr3二者均不可改变



指向指针的指针


int a &#61; 123;
int* b &#61; &a;
int** c &#61; &b;//**c相当于*b&#xff0c;即得到a的值&#xff08;表达式从里向外逐层求值&#xff09;
//*操作符具有从右向左的结合性


野指针



  • 未初始化和非法指针

    //eg&#xff1a;
    int* a;//仅定义了一个指针&#xff0c;其所指的区域不确定
    *a &#61; 12;

    • 定位到一个非法地址&#xff0c;从而终止
    • 定位到一个可以访问的地址&#xff0c;无意修改了它&#xff0c;这样的错误难以捕捉引发的错误可能与原先用于操作的代码完全不相干

    避免方法&#xff1a;用指针进行间接访之前&#xff0c;一定要确保其已经初始化&#xff0c;并被恰当地赋值

  • NULL指针

    一个特殊的指针变量&#xff0c;表示不指向任何东西

    int* a &#61; NULL;

    NULL指针给出一种方法&#xff0c;来表示特定的指针目前未指向任何东西

    注意事项&#xff1a;


    • 对于一个指针&#xff0c;若已经知道将被初始化为什么地址&#xff0c;那么赋给它这个地址值&#xff0c;否则将其设置为NULL
    • 在对任何一个指针进行间接引用前&#xff0c;先判断这个指针是否为空
  • 杜绝“野”指针

    指向“垃圾”内存的指针&#xff0c;if等判断对它们不起作用&#xff0c;因为没有置NULL

    三种情况&#xff1a;


    • 指针变量没有初始化
    • 已经释放不用的指针没有置NULL&#xff0c;如delete和free之后的指针
    • 指针操作超越了变量的作用范围

    注意事项&#xff1a;没有初始化的&#xff0c;不用的或者超出范围的指针将其值置为NULL


5.2 指针的基本操作



&与*操作符


指针类型默认都是四个字节大小&#xff0c;而与其指向的空间的数据类型无关

char ch &#61; &#39;a&#39;;
char* cp &#61; &ch;
//表达式
//*cp
char ch2 &#61; *cp;//作为右值&#xff0c;将a赋给ch2
*cp &#61; &#39;b&#39;;//作为左值&#xff0c;等价于对ch进行操作
//*cp &#43; 1
char ch2 &#61; *cp &#43; 1;//作为右值&#xff0c;将b赋给ch2
*cp &#43; 1 &#61; ...;//作为左值非法
//*(cp &#43; 1)
char ch2 &#61; *(cp &#43; 1);//作为右值&#xff0c;将ch后一个地址的元素的值赋给ch2
*(cp &#43; 1) &#61; &#39;b&#39;;//作为左值&#xff0c;等价于对ch后一个地址的元素进行操作


原始指针的基本运算


&#43;&#43; 与 – 操作符

char* cp2 &#61; &#43;&#43;cp;
/*
mov eax,dword ptr [cp]
add eax,1
mov dword ptr [cp],eax
mov ecx,dword ptr [cp]
mov dword ptr [cp2],ecx
*/
char* cp3 &#61; cp&#43;&#43;;
/*
mov eax,dword ptr [cp]
mov dword ptr [cp3],eax
mov exc,dword ptr [cp]
add ecx,1
mov dword ptr [cp],ecx
*/
// -- 操作同理 add --> sub

*&#43;&#43;p &#61; a;
//相当于对p &#43; 1所指的位置进行赋值操作
*p&#43;&#43; &#61; a;
//相当于对p所指位置进行赋值后p的

关于&#43;&#43;&#43;&#43;&#xff0c;----等运算符

编译器分解程序符号的方法&#xff1a;逐字符读入&#xff0c;若该字符可能组成一个符号&#xff0c;那么读入下一个字符&#xff0c;知道读入的字符不能在组成一个有意义的符号&#xff08;贪心法&#xff09;

int a &#61; 1, b &#61; 2, c, d;
c &#61; a &#43;&#43;&#43; b; // --> (a &#43;&#43;) &#43; b
d &#61; a &#43;&#43;&#43;&#43; b; //error
&#43;&#43;*&#43;&#43;p; // --> &#43;&#43;(*(&#43;&#43;p))

5.3 CPP程序的存储区域划分

#include "stdafx.h"
#include
int a &#61; 0; //(GVAR)全局初始化区
int* p1; //(bss)全局未初始化区
int main() //(text)代码区
int b&#61;1; //(stack)栈区变量
char s[] &#61; "abc"; //(stack)栈区变量
int*p2&#61;NULL; //(stack)栈区变量
char *p3 &#61; "123456"; //123456\\0在常量区, p3在(stack)栈区
static int c &#61; 0; //(GVAR)全局(静态)初始化区
p1 &#61; new int(10); //(heap)堆区变量
p2 &#61; new int(20); //(heap)堆区变量
char* p4 &#61; new char[7]; //(heap)堆区变量
strcpy_s(p4, 7, "123456"); //(text)代码区
//(text)代码区
if (p1 !&#61; NULL)

delete p1;
p1 &#61; NULL;

if (p2 !&#61; NULL)

delete p2;
p2 &#61; NULL;

if (p4 !&#61; NULL)

delete[ ] p4;
p4 &#61; NULL;

//(text)代码区
return 0; //(text)代码区

  • main函数内定义的变量存储在栈区&#xff0c;且后定义的地址更低&#xff1b;
  • new所申请的区域为堆区&#xff0c;后申请的地址更高&#xff1b;
  • 全局变量和static修饰的变量存在全局区
  • 指针指向内容是否可以改变取决于所指向的区域&#xff0c;常量区的值不可改变&#xff0c;栈区可以改变&#xff1b;
  • 其他代码一般存在代码区&#xff1b;


5.4 CPP动态分配和回收原则



动态分配资源&#xff1a;堆&#xff08;heap&#xff09;


程序通常需要涉及三个内存管理器的操作&#xff1a;


  1. 分配一个某个大小的内存块&#xff1b;
  2. 释放一个之前分配的内存块&#xff1b;
  3. 垃圾收集操作。寻找不再使用的内存块并予以释放&#xff1b;

回收策略需要实现性能、实时性、额外开销等方面的平衡&#xff0c;很难有统一和高效的做法。

C&#43;&#43;做了1&#xff0c;2&#xff1b;而Java做了1&#xff0c;3。


5.5 RAII初步

资源管理方案&#xff1a;RAII(Resource Acquisition Is Initialization)

RAII依托析构函数&#xff0c;来对所有的资源&#xff1a;包括堆内存在内进行管理。对RAII的使用&#xff0c;使得C&#43;&#43;不需要类似于Java那样的垃圾收集方法,也能有效地对内存进行管理。RAII 的存在&#xff0c;也是垃圾收集虽然理论上可以在C&#43;&#43;使用&#xff0c;但从来没有真正流行过的主要原因。

RAII比较成熟的智能指针代表&#xff1a;

std::auto_ptr
boost::shared_ptr

5.6 C&#43;&#43;中几种变量的对比



栈和堆中的变量对比




全局静态存储区和常量存储区的变量对比



5.7 内存泄漏



什么是内存泄漏问题&#xff08;memory leak&#xff09;


指程序中己动态分配的堆内存由于某种原因程序未释放或无法释&#xff0c;造成系统内存的浪费&#xff0c;导致程序运行速度减慢甚至系统崩溃等严重后果。



内存泄漏发生原因和排查方式



  1. 内存泄漏主要发生在堆内存分配方式中&#xff0c;即“配置了内存后&#xff0c;所有指
    向该内存的指针都遗失了”。若缺乏语言这样的垃圾回收机制&#xff0c;这样的内存
    片就无法归还系统。
  2. 因为内存泄漏属于程序运行中的问题&#xff0c;无法通过编译识别&#xff0c;所以
    只能在程序运行过程中来判别和诊断。

注意delete 和delete[] 的区别。


5.8 智能指针

使用指针是非常危险的行为&#xff0c;可能存在空指针&#xff0c;野指针问题&#xff0c;并可能造成内存泄漏问题。

可指针又非常的高效&#xff0c;所以我们希望以更安全的方式来使用指针。

两种方案&#xff1a;


  • 使用更安全的指针&#xff1a;智能指针
  • 不使用指针&#xff0c;使用更安全的方式&#xff1a;引用

C&#43; &#43;中推出了四种常用的智能指针:
unique_ ptr、 shared ptr、weak_ ptr 和C&#43;&#43; 11中已经废弃(deprecated)的auto_ ptr,在C&#43;&#43; 17中被正式删除。



auto_ptr


由new expression获得对象&#xff0c;在auto_ ptr 对象销毁时&#xff0c;他所管理的对象也会
自动被delete掉。

所有权转移:不小心把它传递给另外的智能指针&#xff0c;原来的指针就不再拥有这个对象了。在拷贝/赋值过程中&#xff0c;会直接剥夺指针对原对象对内存的控制权&#xff0c;转交给新对象&#xff0c;然后再将原对象指针置为nullptr。

演示代码&#xff1a;

#include
#include <iostream>
#include
using namespace std;
int main()
// 确定auto_ptr失效的范围
// 对int使用
auto_ptr pI(new int(10));
cout <<*pI < // auto_ptr C&#43;&#43; 17中移除 拥有严格对象所有权语义的智能指针
// auto_ptr原理&#xff1a;在拷贝 / 赋值过程中&#xff0c;直接剥夺原对象对内存的控制权&#xff0c;转交给新对象&#xff0c;
// 然后再将原对象指针置为nullptr&#xff08;早期&#xff1a;NULL&#xff09;。这种做法也叫管理权转移。
// 他的缺点不言而喻&#xff0c;当我们再次去访问原对象时&#xff0c;程序就会报错&#xff0c;所以auto_ptr可以说实现的不好&#xff0c;
// 很多企业在其库内也是要求不准使用auto_ptr。
auto_ptr languages[5] &#61;
auto_ptr(new string("C")),
auto_ptr(new string("Java")),
auto_ptr(new string("C&#43;&#43;")),
auto_ptr(new string("Python")),
auto_ptr(new string("Rust"))
;
cout <<"There are some computer languages here first time: \\n";
for (int i &#61; 0; i <5; &#43;&#43;i)

cout <<*languages[i] <
auto_ptr pC;
pC &#61; languages[2]; // languges[2] loses ownership. 将所有权从languges[2]转让给pC&#xff0c;
//此时languges[2]不再引用该字符串从而变成空指针
cout <<"There are some computer languages here second time: \\n";
for (int i &#61; 0; i <2; &#43;&#43;i)

cout <<*languages[i] <
cout <<"The winner is " <<*pC < //cout <<"There are some computer languages here third time: \\n";
//for (int i &#61; 0; i <5; &#43;&#43;i)
//
// cout <<*languages[i] < //

return 0;


unique_ptr


unique_ptr是专属所有权&#xff0c;所以unique_ptr管理的内存&#xff0c;只能被一个对象持有&#xff0c;不支持复制和赋值。

移动语义: unique_ptr禁止了拷贝语义&#xff0c;但有时我们也需要能够转移所有权&#xff0c;于是提供了移动语义&#xff0c;即可以使用std::move()进行控制所有权的转移。

#include
#include
using namespace std;
int main()
// 在这个范围之外&#xff0c;unique_ptr被释放

auto i &#61; unique_ptr(new int(10));
cout <<*i <
// unique_ptr
auto w &#61; std::make_unique(10);
cout <<*(w.get()) < //auto w2 &#61; w; // 编译错误如果想要把 w 复制给 w2, 是不可以的。
// 因为复制从语义上来说&#xff0c;两个对象将共享同一块内存。
// unique_ptr 只支持移动语义, 即如下
auto w2 &#61; std::move(w); // w2 获得内存所有权&#xff0c;w 此时等于 nullptr
cout <<((w.get() !&#61; nullptr) ? (*w.get()) : -1) < cout <<((w2.get() !&#61; nullptr) ? (*w2.get()) : -1) < return 0;


shared_ptr


shared_ptr通过一个引用计数共享一个对象.

shared_ ptr 是为了解决auto_ ptr在对象所有权上的局限性&#xff0c;在使用引用计数的机制上提供了可以共享所有权的智能指针&#xff0c;当然这需要额外的开销。

通过引用计数对引用个数进行记录&#xff0c;当引用计数为0时&#xff0c;该对象没有被使用&#xff0c;可以进行析构。

#include
#include
using namespace std;
int main()
// shared_ptr

//shared_ptr 代表的是共享所有权&#xff0c;即多个 shared_ptr 可以共享同一块内存。
auto wA &#61; shared_ptr(new int(20));

auto wA2 &#61; wA;
cout <<((wA2.get() !&#61; nullptr) ? (*wA2.get()) : -1) < cout <<((wA.get() !&#61; nullptr) ? (*wA.get()) : -1) < cout < cout <
cout < cout < cout <<((wA.get() !&#61; nullptr) ? (*wA.get()) : -1) < //shared_ptr 内部是利用引用计数来实现内存的自动管理&#xff0c;每当复制一个 shared_ptr&#xff0c;
// 引用计数会 &#43; 1。当一个 shared_ptr 离开作用域时&#xff0c;引用计数会 - 1。
// 当引用计数为 0 的时候&#xff0c;则 delete 内存。

// move 语法
auto wAA &#61; std::make_shared(30);
auto wAA2 &#61; std::move(wAA); // 此时 wAA 等于 nullptr&#xff0c;wAA2.use_count() 等于 1
cout <<((wAA.get() !&#61; nullptr) ? (*wAA.get()) : -1) < cout <<((wAA2.get() !&#61; nullptr) ? (*wAA2.get()) : -1) < cout < cout < //将 wAA 对象 move 给 wAA2&#xff0c;意味着 wAA 放弃了对内存的所有权和管理&#xff0c;此时 wAA对象等于 nullptr。
//而 wAA2 获得了对象所有权&#xff0c;但因为此时 wAA 已不再持有对象&#xff0c;因此 wAA2 的引用计数为 1。
return 0;

带来的问题&#xff1a;循环引用

循环引用会导致堆里的内存无法正常回收&#xff0c;造成内存泄漏。



weak_ptr


weak_ ptr 被设计为与shared_ ptr 共同工作&#xff0c;用- -种观察者模式工作。

作用是协助shared_ptr 工作&#xff0c;可获得资源的观测权,像旁观者那样观测资源的使用情况。

观察者意味着weak_ ptr只对shared_ ptr 进行引用&#xff0c;而不改变其引用计数&#xff0c;当被观察的shared_ ptr 失效后&#xff0c;相应的weak_ ptr 也相应失效。

#include
#include
#include
using namespace std;
struct B;
struct A
shared_ptr pb;
~A()

cout <<"~A()" <
;
struct B
shared_ptr pa;
~B()

cout <<"~B()" <
;
// pa 和 pb 存在着循环引用&#xff0c;根据 shared_ptr 引用计数的原理&#xff0c;pa 和 pb 都无法被正常的释放。
// weak_ptr 是为了解决 shared_ptr 双向引用的问题。
struct BW;
struct AW
shared_ptr pb;
~AW()

cout <<"~AW()" <
;
struct BW
weak_ptr pa;
~BW()

cout <<"~BW()" <
;
void Test()
cout <<"Test shared_ptr and shared_ptr: " < shared_ptr
tA(new A()); // 1
shared_ptr tB(new B()); // 1
cout < cout < tA->pb &#61; tB;
tB->pa &#61; tA;
cout < cout <void Test2()
cout <<"Test weak_ptr and shared_ptr: " < shared_ptr tA(new AW());
shared_ptr tB(new BW());
cout < cout < tA->pb &#61; tB;
tB->pa &#61; tA;
cout < cout <int main()
Test();
Test2();
return 0;

5.9 引用

引用&#xff1a;一种特殊的指针&#xff0c;不允许修改的指针。



使用指针有哪些坑&#xff1a;



  1. 空指针&#xff1b;
  2. 野指针&#xff1b;
  3. 不知不觉改变了指针的值&#xff0c;却继续使用&#xff1b;


使用引用&#xff0c;则可以&#xff1a;



  1. 不存在空引用&#xff1b;
  2. 必须初始化&#xff1b;
  3. 一个引用永远指向它初始化的那个对象&#xff1b;


引用的基本使用&#xff1a;可以认为是指定变量的别名&#xff0c;使用时可以认为是变量本身


示例&#xff1a;

int x &#61; 1, x2 &#61; 3;
int& rx &#61; x;
rx &#61; 2;
cout <cout <rX &#61; x2;
cout <cout <


两个问题


有了指针为什么还需要引用?

Bjarne Stroustrup的解释&#xff1a;为了支持函数运算符重载&#xff1b;

有了引用为什么还需要指针?

Bjarne Stroustrup的解释&#xff1a;为了兼容C语言。



补充&#xff1a;关于函数传递参数类型的说明



  • 对内置基础类型(如int,double等)而言&#xff1a;

    在函数中传递时pass by value更高效&#xff1b;

  • 对OO面向对象中自定义类型而言&#xff1a;

    在函数中传递时pass by reference to const更高效。


推荐阅读
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • Python实现变声器功能(萝莉音御姐音)的方法及步骤
    本文介绍了使用Python实现变声器功能(萝莉音御姐音)的方法及步骤。首先登录百度AL开发平台,选择语音合成,创建应用并填写应用信息,获取Appid、API Key和Secret Key。然后安装pythonsdk,可以通过pip install baidu-aip或python setup.py install进行安装。最后,书写代码实现变声器功能,使用AipSpeech库进行语音合成,可以设置音量等参数。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 本文介绍了如何找到并终止在8080端口上运行的进程的方法,通过使用终端命令lsof -i :8080可以获取在该端口上运行的所有进程的输出,并使用kill命令终止指定进程的运行。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
author-avatar
追梦and寻梦
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有